﻿using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using Unread.Business.Data.Opml.Interfaces;
using Unread.Business.Data.Opml.Model;
using Unread.Business.Data.Storage.Interfaces;
using Unread.Business.Models;
using Unread.Business.Repositories.Interfaces;

namespace Unread.Business.Repositories
{
    [Export(typeof(IFeedRepository))]
    internal class FeedRepository : IFeedRepository
    {
        private readonly IRoamingStorageContext _roamingStorageContext;
        private readonly IOpmlReader _opmlReader;
        private readonly IOpmlWriter _opmlWriter;

        [ImportingConstructor]
        public FeedRepository(IRoamingStorageContext roamingStorageContext, IOpmlReader opmlReader, IOpmlWriter opmlWriter)
        {
            if (roamingStorageContext == null) throw new ArgumentNullException("roamingStorageContext");
            if (opmlReader == null) throw new ArgumentNullException("opmlReader");
            if (opmlWriter == null) throw new ArgumentNullException("opmlWriter");

            _roamingStorageContext = roamingStorageContext;
            _opmlReader = opmlReader;
            _opmlWriter = opmlWriter;
        }

        public async Task<IEnumerable<Feed>> GetAll()
        {
            var opml = await LoadOpml();

            return Feed.MapOutlinesToFeeds(opml.Body.Outlines);
        }

        public async Task<Feed> GetById(Guid feedId)
        {
            var opml = await LoadOpml();

            return Feed.MapOutlinesToFeeds(opml.Body.Outlines)
                .SingleOrDefault(f => f.Id == feedId);
        }

        public async Task Create(Feed feed)
        {
            if (feed == null) throw new ArgumentNullException("feed");

            var feedAsOutline = Feed.MapFeedToOutline(feed);

            var opml = await LoadOpml();
            opml.Body.Outlines.Add(feedAsOutline);

            var opmlAsString = _opmlWriter.Write(opml);

            await _roamingStorageContext.WriteFile(opmlAsString);
        }

        public async Task Update(Feed feed)
        {
            if (feed == null) throw new ArgumentNullException("feed");

            var opml = await LoadOpml();
            
            var outlineToUpdate = opml.Body.Outlines.SingleOrDefault(o => o.Id == feed.Id);
            if (outlineToUpdate == null)
                throw new InvalidOperationException(string.Format("Outline not found by id '{0}'", feed.Id));

            var feedAsOutline = Feed.MapFeedToOutline(feed);
            opml.Body.Outlines[opml.Body.Outlines.IndexOf(outlineToUpdate)] = Feed.MapFeedToOutline(feed);

            var opmlAsString = _opmlWriter.Write(opml);
            await _roamingStorageContext.WriteFile(opmlAsString);
        }

        public async Task Delete(Feed feed)
        {
            if (feed == null) throw new ArgumentNullException("feed");

            var opml = await LoadOpml();

            var outlineToDelete = opml.Body.Outlines.SingleOrDefault(o => o.Id == feed.Id);
            if (outlineToDelete == null)
                throw new InvalidOperationException(string.Format("Outline not found by id '{0}'", feed.Id));

            var feedAsOutline = Feed.MapFeedToOutline(feed);
            opml.Body.Outlines.RemoveAt(opml.Body.Outlines.IndexOf(outlineToDelete));

            var opmlAsString = _opmlWriter.Write(opml);
            await _roamingStorageContext.WriteFile(opmlAsString);
        }

        private async Task<Root> LoadOpml()
        {
            Root opml = null;

            var feedsFileContent = await _roamingStorageContext.ReadFile();

            if (!string.IsNullOrWhiteSpace(feedsFileContent))
                opml = _opmlReader.Read(feedsFileContent);
            else
                opml = new Root { Head = new Head { Title = "unread feeds" } };
            return opml;
        }
    }
}